文章未來將更新於:
https://kevinyay945.com/golang-project-design/2023/introduce-golang-mock-tool/
這次我們使用的是mock這個工具幫我們快速的模擬需要的interface
值得一提的是,因為這次要撰寫文章特別又回去看了文件,才發先原先golang提供的mock的github被archive了,現在是由uber來接管這個專案,所以如果未來大家要使用,要特別注意,不要用到archive的連結了
舊的: https://github.com/golang/mock
新的: https://github.com/uber-go/mock
要使用這個工具,要先瞭解他的原理
他的主要邏輯是,他可以針對你目前的interface透過工具的幫忙,自動產生出可以implement相對應的資料的內容
比如說,我先宣告了一個FileStorer的interface
package application
type FileStorer interface {
UploadAsset(filename string, i []byte, s string) (file domain.CloudFile, err error)
GetPreviewLink(asset domain.CloudFile) (link string, err error)
}
接著透過mock工具提供的cli
打上
mockgen -destination=file_store_mock.go -package=application -self_package=2023_asset_management/application . FileStorer
透過這個指令,他就可以幫你產生相對應的檔案
// Code generated by MockGen. DO NOT EDIT.
// Source: 2023_asset_management/application (interfaces: FileStorer)
// Package application is a generated GoMock package.
package application
import (
domain "2023_asset_management/domain"
reflect "reflect"
gomock "go.uber.org/mock/gomock"
)
// MockFileStorer is a mock of FileStorer interface.
type MockFileStorer struct {
ctrl *gomock.Controller
recorder *MockFileStorerMockRecorder
}
// MockFileStorerMockRecorder is the mock recorder for MockFileStorer.
type MockFileStorerMockRecorder struct {
mock *MockFileStorer
}
// NewMockFileStorer creates a new mock instance.
func NewMockFileStorer(ctrl *gomock.Controller) *MockFileStorer {
mock := &MockFileStorer{ctrl: ctrl}
mock.recorder = &MockFileStorerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockFileStorer) EXPECT() *MockFileStorerMockRecorder {
return m.recorder
}
// GetPreviewLink mocks base method.
func (m *MockFileStorer) GetPreviewLink(arg0 domain.CloudFile) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetPreviewLink", arg0)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetPreviewLink indicates an expected call of GetPreviewLink.
func (mr *MockFileStorerMockRecorder) GetPreviewLink(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPreviewLink", reflect.TypeOf((*MockFileStorer)(nil).GetPreviewLink), arg0)
}
// UploadAsset mocks base method.
func (m *MockFileStorer) UploadAsset(arg0 string, arg1 []byte, arg2 string) (domain.CloudFile, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UploadAsset", arg0, arg1, arg2)
ret0, _ := ret[0].(domain.CloudFile)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UploadAsset indicates an expected call of UploadAsset.
func (mr *MockFileStorerMockRecorder) UploadAsset(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UploadAsset", reflect.TypeOf((*MockFileStorer)(nil).UploadAsset), arg0, arg1, arg2)
}
他可以自動產生一個implement你指定interface的struct,並提供你在測試的時候需要的功能
當然,因為在每個interface下你都會需要這段工具,所以這時候就要使用go提供的一個工具
generate
當你在程式碼中放上這樣的一段內容
//go:generate mockgen -destination=file_store_mock.go -package=application -self_package=2023_asset_management/application . FileStorer
type FileStorer interface {
UploadAsset(filename string, i []byte, s string) (file domain.CloudFile, err error)
GetPreviewLink(asset domain.CloudFile) (link string, err error)
}
並且透過shell來執行以下指令
go generate ./filestore.go
如此一來,他就會去掃描你指定的這個檔案有沒有使用go:generate
這個tag,並且執行後面所撰寫的程式碼內容
如此一來,就算之後有大量的interface需要產生mock file,也可以透過go generate來幫忙快速產生
更棒的是,go generate還有提供另一個方式,可以讓你可以你當前目錄下的所有檔案
方法如下
go generate ./...
如此一來,你只要整個專案都有放上go:generate的內容,都會被順利執行,相當的方便